from datetime import datetime
import requests, json, configparser, os, time
import pypyodbc, subprocess, sys
import mywx,os

plc_ips = {
    'w1': '10.10.100.1',
    'w2': '10.10.100.2',
    'w4': '192.168.1.2',
    'w5': '192.168.1.2',
    'w6': '192.168.1.2',
    'w7': '10.10.100.1',
    'w9': '10.10.155.50',
    'w10': '10.10.100.4',
}


# 定义数据库连接
def mdb_conn(db_name, password=""):
    """
    功能：创建数据库连接
    :param db_name: 数据库名称
    :param db_name: 数据库密码，默认为空
    :return: 返回数据库连接
    """
    str = 'Driver={Microsoft Access Driver (*.mdb)};PWD' + password + ";DBQ=" + db_name
    conn = pypyodbc.win_connect_mdb(str)
    return conn


def mdb_sel(cur, sql):
    """
    功能：向数据库查询数据
    :param cur: 游标
    :param sql: sql语句
    :return: 查询结果集
    """
    try:
        cur.execute(sql)
        return cur.fetchall()
    except:
        return []


def check_plc(ip):
    '''
    本程序用于ping PLC的ip地址
    :return: 连接PLC失败则返回False，正常则返回True
    '''
    result = subprocess.call('ping -w 1000 -n 1 %s' % ip, stdout=subprocess.PIPE, shell=True)
    if result == 0:
        h = subprocess.getoutput('ping ' + ip)
        returnnum = h.split('平均 = ')[1]
        info = ('%s 能ping通，延迟平均值为：%s' % (ip, returnnum))
        print('%s 能ping通，延迟平均值为：%s' % (ip, returnnum))
        print("到PLC的网络正常！")
        return True
    else:
        print('%s ping 不通！' % ip)
        print("网络故障！")
        return False


def check_db(access_cur, tablename):
    '''
    本程序用于查找最后一条数据的时间，与现在多少秒，用于判断PLC的数据是否正常上传
    :param access_cur: 数据库的指针
    :param tablename: 数据库的表名
    :return: 正数则是正常，-1表示异常
    '''
    sql = "SELECT DISTINCT AlarmDate,AlarmTime,AlarmType,VarName FROM {0} ORDER BY AlarmDate DESC, AlarmTime DESC".format(
        tablename)
    data = mdb_sel(access_cur, sql)
    if data:
        last_data = data[0]
        last_time = last_data[0].strip() + " " + last_data[1].strip()
        last = datetime.strptime(last_time, "%y/%m/%d %H:%M:%S")
        second = (datetime.now() - last).seconds
    else:
        second = -1
    return second


def read_ini(filename='config.ini'):
    '''
    本函数读取ini文件
    :param filename: 文件名
    :return: 返回变量line(线体名称),path（数据库位置），onetime（每次上传数据的数量）
    '''
    conf = configparser.ConfigParser()
    conf.read(filename)
    line = conf['DEFAULT']['line']
    path = conf['DEFAULT']['path']
    onetime = int(conf['DEFAULT']['onetime'])
    program = conf['DEFAULT']['program']
    return line, path, onetime, program


def status(tablename, startby):
    '''
    用于上传终端状态
    :param tablename: 数据库的表名
    :param startby: 数据开始上传的位置
    :return: 返回PLC的状态(连接PLC失败则返回False，正常则返回True)，以及时间间隔
    '''
    status_url = 'http://106.52.146.218:8081/status/'
    line, path, onetime, program = read_ini()
    # 网络状态，能上传都是True
    network = True
    # PLC的状态，能不能Ping通
    PLC = check_plc(plc_ips[line])
    # 连接本地access数据库
    access_conn = mdb_conn(path)
    access_cur = access_conn.cursor()
    # 获得最后一条数据的时间间隔
    db_interval = check_db(access_cur, tablename)
    # 定义request的get参数
    params = {'line': line, 'network': network, 'PLC': PLC, 'db_interval': db_interval, 'startby': startby}
    r = requests.get(status_url, params=params)
    print('状态上传已成功~~~')
    return PLC, db_interval


def get_startby(line):
    '''
    获得在服务器中获得startby参数
    :param line:线体名称
    :return:在服务器最后一个startby的值
    '''
    status_url = 'http://106.52.146.218:8081/status/{0}'.format(line)
    times = 0
    while True:
        r = requests.get(status_url)
        if r.status_code == 200:
            data = r.json()
            return int(data[0])
        else:
            times = times + 1
            print('获取状态重试第{0}次。。。。'.format(times))
            if times > 3:
                print('获取状态失败！！！！')
                break


def upload_list(line, data, url='http://106.52.146.218:8081/upload/'):
    '''
    批量上传数据
    :param line:哪条线体
    :param d:待上传数据
    :return:
    '''
    # 数据库API的url
    # url = 'http://47.113.124.50:8080/upload/'
    # 完成上传的标记，原始为False,上传后status返回200则设置为True
    flag = False
    # 上传重试次数
    times = 0
    # 初始化一个上传数据的空列表
    data_list = []
    # 遍历待上传的数据
    for d in data:
        # 标准化时间格式
        Dtime = (d[0].strip() + " " + d[1].strip()).strip()
        # 预处理待上传数据的格式为字典
        try:
            my_data = {'line': line, 'AlarmDate': d[0].strip(), 'AlarmTime': d[1].strip(), 'AlarmType': d[2].strip(),
                       'VarName': d[3].strip(), 'DateTime': Dtime}
            # 加入待上传的数据列表
            data_list.append(my_data)
        except:
            print('有一条数据有错误')
    # 完成上传的标记，原始为False,上传后status返回200则设置为True
    while not flag:
        # 上传数据
        r = requests.post(url, data=json.dumps(data_list))
        if r.status_code == 200:
            flag = True
        else:
            times = + 1
            print('上传失败，重新第{0}次上传！！！'.format(times))
            time.sleep(10)


def upload(startby, count):
    '''
    分批上传上传
    :param startby:数据库的开始位置
    :param count:数据库的结束位置（最后一个数据）
    :return:循环上传，直到上传完为止
    '''
    while True:
        print('起点是：', startby)
        if startby < count:
            # 查询数据库内所有的数据
            data = mdb_sel(access_cur, sql)
            print('已取得{0}条数据'.format(len(data)))
            # startby是待上传数据的起点，由config.ini决定
            print('现在的起点是：', startby)
            # 将上传的数据切片，一次性上传太多数据，太慢
            up_data = data[startby:startby + onetime]
            upload_list(line, up_data)
            print('已上传{0}条数据~~~~'.format(len(up_data)))
            # 上传数据后，总上传数据的超过总长度，则数据库总长度为开始，为下次做准备
            if startby + onetime < count:
                startby = startby + onetime
            else:
                startby = count
        else:
            startby = count
            print('所有数据已上传完毕~~~~', '等待60秒~~~~')
            break
    return startby


def restart_kv(program):
    print('组态王无法正常记录，请重新启动组态王！！！')
    print('正在重启组态王~~~')
    try:
        mywx.kill_pocess()
    except:
        pass
    time.sleep(5)
    for i in range(2):
        os.system("kingview.bat")
        time.sleep(5)



if __name__ == '__main__':
    # 确认组态王是否已经开启
    # if mywx.FindWindow("运行系统",False):
    #     isopen = True
    # elif mywx.FindWindow("CELL监控系统运行中...", False):
    #     isopen = True
    # else:
    #     isopen = False
    #     # 自动启动组态王
    #     os.system("kingview.bat")
    #     time.sleep(10)
    # 如果组态王正常
    if True:
        print("组态王正常开启")
        # 取得/初始化config.ini参数
        line, path, onetime, program = read_ini()
        # 连接本地access数据库
        access_conn = mdb_conn(path)
        access_cur = access_conn.cursor()
        # 不带参数时，默认实时上传数据
        if len(sys.argv) == 1:
            # 获得startby参数
            startby = get_startby(line)
            # 确定表的名字
            month = datetime.today().month
            if month > 9:
                tablename = 'Alarm{0}{1}'.format(datetime.today().year, month)
            else:
                tablename = 'Alarm{0}0{1}'.format(datetime.today().year, month)
            # 获得数据表里面的数据
            sql = "SELECT DISTINCT AlarmDate,AlarmTime,AlarmType,VarName FROM {0} ORDER BY AlarmDate, AlarmTime".format(
                tablename)
            mdb_data = mdb_sel(access_cur, sql)
            count = len(mdb_data)
            print('数据表里合计有{0}条有效数据'.format(count))
            # 上传数据，并获得完成上传后的startby数据
            startby = upload(startby, count)
            # 检查PLC状态并上传
            PLC_status, db_interval = status(tablename, startby)
            # 若PLC能ping通，而且db_interval大于300秒（5分钟）
            if PLC_status:
                if db_interval > 300:
                    # 重启组态王
                    restart_kv(program)
                elif db_interval > 200:
                    print('已经有{0}秒没有取得数据。'.format(db_interval))
            else:
                print('PLC网络不通！！！')
        elif len(sys.argv) == 2:
            if int(sys.argv[1]) > 9:
                tablename = 'Alarm{0}{1}'.format(datetime.today().year, sys.argv[1])
            else:
                tablename = 'Alarm{0}0{1}'.format(datetime.today().year, sys.argv[1])
            startby = 0
            # 获得数据表里面的数据
            sql = "SELECT DISTINCT AlarmDate,AlarmTime,AlarmType,VarName FROM {0} ORDER BY AlarmDate, AlarmTime".format(
                tablename)
            mdb_data = mdb_sel(access_cur, sql)
            upload_list(line, mdb_data)
            print("已完成%s线数据库%s表格的上传，合计上传%s条数据" % (line, tablename, len(mdb_data)))
            print("数据库的文件目录是：%s" % path)
            status(tablename, len(mdb_data))
    else:
        for i in range(2):
            os.system("kingview.bat")
            time.sleep(5)
